package com.bocek.chakmapoint.model;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.geom.Ellipse2D;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * An oval slide item.
 * 
 * @author Baris Sencan
 * @version 2.0
 */
public class COval extends Ellipse2D.Double implements SlideItem {
	private static final long serialVersionUID = -8417425848032880827L;
	private Point p1, p2;
	private Selection section;
	private Color color;
	private int thickness;
	private boolean selected;
	private boolean filled = false;
	private Point startPoint;
	private String name;

	public COval(Point p) {
		this();
		this.startPoint = p;
	}

	public void addPoint(Point p) {
		int firstX = (int) startPoint.getX();
		int firstY = (int) startPoint.getY();

		int secondX = (int) p.getX();
		int secondY = (int) p.getY();

		int width = Math.abs(firstX - secondX);
		int height = Math.abs(firstY - secondY);

		if ((firstX > secondX) && (firstY <= secondY)) {
			this.setFrame(secondX, firstY, width, height);
		} else if ((firstX <= secondX) && (firstY > secondY)) {
			this.setFrame(firstX, secondY, width, height);
		} else if ((firstX > secondX) && (firstY > secondY)) {
			this.setFrame(secondX, secondY, width, height);
		} else {
			this.setFrame(firstX, firstY, width, height);
		}
	}

	/**
	 * Default constructor.
	 * 
	 * @requires Nothing.
	 * @modifies this
	 * @effects Returns a new COval object.
	 */
	public COval() {
		this(.0D, .0D, .0D, .0D, Color.BLACK, 1);
	}

	/**
	 * Constructor with custom color.
	 * 
	 * @requires color != null
	 * @modifies this
	 * @effects Returns a new COval object.
	 */
	public COval(Color color) {
		this(.0D, .0D, .0D, .0D, color, 1);
	}

	/**
	 * Constructor with custom thickness.
	 * 
	 * @requires thickness > 0
	 * @modifies this
	 * @effects Returns a new COval object.
	 */
	public COval(int thickness) {
		this(.0D, .0D, .0D, .0D, Color.BLACK, thickness);
	}

	/**
	 * Constructor with custom color and thickness.
	 * 
	 * @requires color != null && thickness > 0
	 * @modifies this
	 * @effects Returns a new COval object.
	 */
	public COval(Color color, int thickness) {
		this(.0D, .0D, .0D, .0D, color, thickness);
	}

	/**
	 * Constructor with all attributes custom.
	 * 
	 * @requires x >= 0 && y >= 0 && width >= 0 && y >= 0 && color != null &&
	 *           thickness > 0
	 * @modifies this
	 * @effects Returns a new COval object.
	 */
	public COval(double x, double y, double width, double height, Color color,
			int thickness) {
		super(x, y, width, height);
		p1 = new Point((int) x, (int) y);
		p2 = new Point((int) (x + width), (int) (y + height));
		this.color = color;
		this.thickness = thickness;
		selection = Selection.NONE;
		selected = false;
		name = "Oval";
		
		if (!repOK())
			System.err.println("COval repOK returned false.");
	}

	public boolean repOK() {
		boolean ok = true;

		ok &= (p1 != null);
		ok &= (p2 != null);
		ok &= (color != null);
		ok &= (thickness > 0);

		return ok;
	}

	/**
	 * Thickness attribute setter.
	 * 
	 * @requires thickness > 0
	 * @modifies this
	 * @effects Nothing.
	 */
	public void setThickness(int thickness) {
		this.thickness = thickness;

		if (!repOK())
			System.err.println("COval repOK returned false.");
	}

	/**
	 * Thickness attribute getter.
	 * 
	 * @requires Nothing.
	 * @modifies Nothing.
	 * @effects Returns thickness such that thickness > 0.
	 */
	public int getThickness() {
		return thickness;
	}

	public void setFilled(boolean b) {
		this.filled = b;
	}

	public boolean isFilled() {
		return this.filled;
	}

	@Override
	public String getName() {
		return this.name;
	}

	@Override
	public void draw(Graphics g) {
		Graphics2D g2d = (Graphics2D) g;
		Color previousColor = g2d.getColor();
		Stroke previousStroke = g2d.getStroke();

		g2d.setColor(color);
		g2d.setStroke(new BasicStroke(thickness));

		if (isFilled())
			g2d.fill((Ellipse2D.Double) this);
		else
			g2d.draw((Ellipse2D.Double) this);

		if (isSelected()) {
			// dashed line
			g2d.setStroke(new BasicStroke(2, BasicStroke.CAP_ROUND,
					BasicStroke.JOIN_ROUND, 0.0f, new float[] { 4.0f, 4.0f },
					0.9f));
			g2d.setColor(Color.BLUE);
			g2d.draw(this.drawSelection());
		}

		g2d.setStroke(previousStroke);
		g2d.setColor(previousColor);

		if (!repOK())
			System.err.println("COval repOK returned false.");
	}

	@Override
	public boolean isPointInside(Point p) {
		return contains(p);
	}

	@Override
	public boolean select(Point p) {

		/*if (p1.distance(p) <= thickness + 3) {
			selection = Selection.LEFT;
			this.selected = true;
		}

		else if (p2.distance(p) <= thickness + 3) {
			selection = Selection.RIGHT;
			this.selected = true;
		} else */if (isPointInside(p)) {
			selection = Selection.ALL;
			this.selected = true;
		}

		if (selected)
			return true;
		return false;
	}

	@Override
	public boolean deselect() {

		if (selected) {
			selection = Selection.NONE;
			this.selected = false;
			return true;
		}

		return false;
	}

	@Override
	public boolean isSelected() {
		return (selected);
	}

	@Override
	public void move(Point delta) {

		if (selection == Selection.ALL) {
			setFrame(getX() + delta.getX(), getY() + delta.getY(), getWidth(),
					getHeight());
		}/* else if (selection == Selection.LEFT || selection == Selection.RIGHT) {
			double minX, minY, maxX, maxY;
			Point selectedP = p1;

			if (selection != Selection.LEFT)
				selectedP = p2;
			selectedP.setLocation(selectedP.getX() + delta.getX(),
					selectedP.getY() + delta.getY());

			if (p1.getX() < p2.getX()) {
				minX = p1.getX();
				maxX = p2.getX();
			} else {
				minX = p2.getX();
				maxX = p1.getX();
			}

			if (p1.getY() < p2.getY()) {
				minY = p1.getY();
				maxY = p2.getY();
			} else {
				minY = p2.getY();
				maxY = p1.getY();
			}

			setFrame(minX, minY, maxX - minX, maxY - minY);
		}*/

		if (!repOK())
			System.err.println("COval repOK returned false.");
	}

	@Override
	public boolean isOutsideFrame(Dimension frameDimension) {
		boolean outside = false;

		outside |= getMinX() < 0;
		outside |= getMinY() < 0;
		outside |= getMaxX() > frameDimension.getWidth();
		outside |= getMaxY() > frameDimension.getHeight();

		return outside;
	}

	@Override
	public void setColor(Color c) {
		color = c;

		if (!repOK())
			System.err.println("COval repOK returned false.");
	}

	@Override
	public Color getColor() {
		return color;
	}

	@Override
	public void setSelected(boolean b) {
		this.selected = b;
		this.selection = Selection.ALL;
	}

	@Override
	public Rectangle drawSelection() {
		Rectangle r = new Rectangle(this.getBounds());
		r.setBounds(r.x - 2, r.y - 2, r.width + 2, r.height + 2);
		return r;
	}
	
	@Override
	public void fill() {
		if (isFilled()) {
			setFilled(false);
		}
		else {
			setFilled(true);
		}
	}

	@Override
	public Element saveToXML(Document doc, int depth) {
		/*
		 thickness="" color="" width="3" height="2" isFilled="false" depth="5" centerX="1" centerY="2"
		*/
		
		// DEFAULT
		Element e = doc.createElement("elipse");
		e.setAttribute("thickness", Integer.toString(this.thickness));
		e.setAttribute("color", Integer.toString(this.color.getRGB()));
		e.setAttribute("width", Integer.toString((int) this.getWidth()));
		e.setAttribute("height", Integer.toString((int) this.getHeight()));
		e.setAttribute("isFilled", Boolean.toString(this.filled));
		e.setAttribute("depth", Integer.toString(depth));
		e.setAttribute("centerX", Integer.toString((int) this.getCenterX()));
		e.setAttribute("centerY", Integer.toString((int) this.getCenterY()));
		
		return e;
	}

	@Override
	public boolean intersects(Rectangle r) {
		return super.intersects(r);
	}
}
